package learningApp.Function;


import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;



import org.w3c.dom.*;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;

import learningApp.Accessories.Chi;
import learningApp.Accessories.ChiValue;
import learningApp.Accessories.VariableAssignment;

import org.xml.sax.SAXException;

public class XmlDefinedFunction implements IConcentratedFunction {

	private ArrayList<Monom> _coefficientList;
	private boolean _isInitialized;
	private int _N;
	
	private final String FUNCTIONS = "functions";
	private final String FUNCTION = "function";
	private final String MONOM = "monom";
	private final String COEFFICIENT = "C";
	private final String ALPHA = "alpha";
	private final String ID = "id";
	
	/**
	 * 
	 * @param xmlFilePath
	 */
	public XmlDefinedFunction(String xmlFilePath, int N)
	{
		_coefficientList = GetCoefficientsFromXml(xmlFilePath); 
		_N = N;
		if(_coefficientList == null)
		{
			_isInitialized = false;
			return;
		}
		else
		{
			_isInitialized = true;
		}
	}
	
	public XmlDefinedFunction(InputStream inputStream, int N)
	{
		_coefficientList = GetCoefficientsFromXml(inputStream); 
		_N = N;
		if(_coefficientList == null)
		{
			_isInitialized = false;
			return;
		}
		else
		{
			_isInitialized = true;
		}
	}

//	public XmlDefinedFunction()
//	{
//		
//		_N=5;
//		_isInitialized = false;
//	}
	
	@Override
	public double Query(VariableAssignment assignment)
	{
		Set<Integer> id;
		double value;
		double sum = 0.0;
		
		for(Monom m : _coefficientList)
		{
			id = m.GetCoefficientID();
			value = m.GetCoefficientValue();
			//	Count the number of values in "Monom" which are assigned PLUS
			//	EVEN ==> +
			//	ODD ==> -
			Chi tempChi = new Chi(ChiValue.PLUS);
			
			for(int monomItem : id)
			{
				
				tempChi.Multiply(assignment.GetValueAtIndex(monomItem));
			}

			try {
				sum += value * tempChi.GetChiValueAsInt();
			} catch (Exception e) {
				System.out.println("Unable to calculate monom value");
				return 0.0;
			}
			
		}
		return sum;
	}

	@Override
	public boolean IsInitialized() {
		return _isInitialized;
	}
	
	
	
	/**
	 * Read XML file and load the requested function.
	 * @param xmlFilePath
	 * @return
	 */
	private ArrayList<Monom> GetCoefficientsFromXml(String xmlFilePath) {
		
	////
	////	Resource:	http://www.developerfusion.com/code/2064/a-simple-way-to-read-an-xml-file-in-java/
	//// 	
        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder;
        Document doc;
		try {
			docBuilder = docBuilderFactory.newDocumentBuilder();
			doc = docBuilder.parse (new File(xmlFilePath));
		} catch (ParserConfigurationException e) {
			return null;
		} catch (SAXException e) {
			return null;
		} catch (IOException e) {
			return null;
		}
	    return GetCoefficientsFromXml(doc);

	}
	private ArrayList<Monom> GetCoefficientsFromXml(InputStream inputStream)
	{
	    	
            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder;
            Document doc;
            
			try {
				docBuilder = docBuilderFactory.newDocumentBuilder();

				doc = docBuilder.parse (inputStream);
			} catch (SAXException e) {
				return null;
			} catch (IOException e) {
				return null;
			} catch (ParserConfigurationException e1) {
				return null;
			}
            
            return GetCoefficientsFromXml(doc);

	}
	
	
	private ArrayList<Monom> GetCoefficientsFromXml(Document xmlDoc)
	{
		ArrayList<Monom> polynom = new ArrayList<Monom>();
		try
		{
		
        // normalize text representation
		xmlDoc.getDocumentElement().normalize();

        NodeList mainNode = xmlDoc.getElementsByTagName(FUNCTIONS);
        if(mainNode.getLength() != 1)
        {
        	System.out.println("Invalid XML");
        	return null;
        }
        
        if(mainNode.item(0).getNodeType() != Node.ELEMENT_NODE)
        {
        	//	PROBLEM
        	System.out.println("Invalid XML");
        	return null;
        }
        Element functionsNode = (Element) mainNode.item(0);
        int selectedFunctionId = Integer.parseInt(functionsNode.getAttribute(ID));
        NodeList functionList = functionsNode.getChildNodes();
        if(functionList == null)
        {
        	//	PROBLEM
        	return null;
        }
        
        for(int i=0; i<functionList.getLength(); i++)
        {
        	if(functionList.item(i).getNodeType() != Node.ELEMENT_NODE)
        	{
        		continue;
        	}
        	Element currentElement = (Element) functionList.item(i);
        	if(currentElement.getTagName() != FUNCTION)
        	{
        		continue;
        	}
        	String currentIdStr = currentElement.getAttribute(ID);
        	int currentId = Integer.parseInt(currentIdStr);	//	TRY!!
        	if(currentId != selectedFunctionId)
        	{
        		continue;
        	}
        	//	We found the correct function!
        	System.out.println("Required function found: "+currentId);
        	NodeList monomList = currentElement.getElementsByTagName(MONOM);
        	if(monomList == null)
        	{
        		break;	//	Empty?
        	}
        	for(int monomIndex=0; monomIndex<monomList.getLength(); monomIndex++)
        	{
        		if(monomList.item(monomIndex).getNodeType() != Node.ELEMENT_NODE)
        		{
        			continue;
        		}
        		Element monomElement = (Element)monomList.item(monomIndex);
        		Monom elementAsMonom = GetMonomFromElement(monomElement);
        		if(elementAsMonom != null)
        		{
        			polynom.add(elementAsMonom);
        		}
        		
        	}
        	break;
        }
        return polynom;
        


    }catch (Throwable t) {
    t.printStackTrace ();
    }
	
	return null;
	
	}
	
	@Override
	public int GetDimension() {
		return _N;
	}

	private Monom GetMonomFromElement(Element monomElement) {
		
		NodeList coefficientNode = monomElement.getElementsByTagName(COEFFICIENT);
		if(coefficientNode == null || coefficientNode.getLength() != 1)
		{
			return null;
		}
        if(coefficientNode.item(0).getNodeType() != Node.ELEMENT_NODE)
        {
        	//	PROBLEM
        	System.out.println("Invalid XML");
        	return null;
        }
        Element valueElement = (Element) coefficientNode.item(0);
        
        String cNodeValue = valueElement.getTextContent();
        if(cNodeValue == null)
        {
        	return null;
        }
        
        double value = Double.parseDouble(cNodeValue);
        
        //int monomID = 0;
        Set<Integer> monomID = new HashSet<Integer>();
        NodeList alphaNode = monomElement.getElementsByTagName(ALPHA);
        if(alphaNode == null)
        {
        	return null;
        }
    	for(int alphaIndex=0; alphaIndex<alphaNode.getLength(); alphaIndex++)
    	{
    		if(alphaNode.item(alphaIndex).getNodeType() != Node.ELEMENT_NODE)
    		{
    			return null;
    		}
    		Element alphaElement = (Element)alphaNode.item(alphaIndex);
    		
    		//monomID += Math.pow(2, Integer.parseInt(alphaElement.getTextContent()) - 1);	//	Assume alpha>0;
    		monomID.add(Integer.parseInt(alphaElement.getTextContent()));
    	}
		return new Monom(value, monomID);
	}


}
